package com.walle.ak47.commons.redis;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisException;

/**
 * Redis 哈希
 */
public class RedisHashMap {

    public static Logger logger = Logger.getLogger(RedisHashMap.class);
	
    private JedisPool pool;
    private String mapName;
    private int cacheSeconds ;

    public RedisHashMap(JedisPool pool, String name, int cacheSeconds) {
        this.pool = pool;
        this.mapName = name;
        if (cacheSeconds > 0) {
        	this.cacheSeconds = cacheSeconds ;
        }
    }
    
    public void release(Jedis jedis, boolean isBroken) {
        if (jedis != null) {
            if (isBroken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    /**
     * 获取指定属性值 (已测试)
     *
     * @param key 属性名
     *
     * @return 属性值
     */
    public String get( String key) {
    	
    	String result ;
    	
    	boolean isBroken = false;
        Jedis jedis = null;
        try
        {
	        jedis = pool.getResource() ;
	        result = jedis.hget(mapName, key);
        }catch (JedisException e) {
            isBroken = true;
            logger.warn("failed:size(" + mapName + "),key="+key, e);
            throw e;
        } finally {
            release(jedis, isBroken);
        }
	
        return result;
    	
    }

    /**
     * 获取指定属性值 (已测试)
     *
     * @param keys 属性名
     *
     * @return 属性值
     */
    public List<String> get(final String... keys) 
    {

    	List<String> result ;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = jedis.hmget(mapName, keys);
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:get(" + mapName + "),keys="+keys, e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
		
		return result;
    }

    /**
     * 设置属性 (已测试)
     *
     * @param key 属性名
     * @param value 属性值
     */
    public void put( String key, String value) 
    {

    	boolean isBroken = false;
        Jedis jedis = null;
        try
        {
	        jedis = pool.getResource() ;
	        jedis.hset(mapName, key, value);
	        if ( jedis.ttl( mapName ) <= 0 ) 
	                jedis.expire( mapName, this.cacheSeconds) ;
        }
        catch (JedisException e) {
            isBroken = true;
            logger.warn("failed:put(" + mapName + "),key="+key+",value="+value, e);
            throw e;
        } 
        finally 
        {
            release(jedis, isBroken);
        }
		
    }

    /**
     * 仅当属性名不存在是设置属性
     *
     * @param key 属性名
     * @param value 属性值
     *
     * @return 0表示属性已存在
     */
    public int setOnlyIfNotExists( String key, String value) 
    {
    	int result = -1 ;
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = jedis.hsetnx(mapName, key, value).intValue();
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:setOnlyIfNotExists(" + mapName + ");key="+key+",value="+value, e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
		
    	return result;
    }

    /**
     * 保存多个属性名和属性值 (已测试)
     *
     * @param map 属性
     */
    public void putAll( Map<String, String> map) {
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			jedis.hmset(mapName, map);
			if ( jedis.ttl( mapName ) <= 0 ) 
				jedis.expire( mapName, this.cacheSeconds) ;
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:putAll(" + mapName + ");map="+map.toString(), e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
		
    }

    /**
     * 删除一个或多个属性 (已测试)
     *
     * @param keys 属性名
     *
     * @return 被删除的属性数量
     * 
     */
    public int remove( String... keys) 
    {
    	
    	int index = 0;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			index = jedis.hdel(mapName, keys).intValue();
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:remove(" + mapName + ");keys="+keys, e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
    	
    	return index;
    }

    /**
     * 列出所有属性
     *
     * @return 所有属性名
     */
    public List<String> keys() 
    {
    
    	List<String> result ;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = new ArrayList<>(jedis.hkeys(mapName));
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:keys(" + mapName + ");", e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
    	
    	return result;
    }
    
    /**
     * 列出所有属性
     *
     * @return 所有属性名
     */
    public List<String> containsValue( String value ) 
    {
    
    	List<String> result ;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = new ArrayList<>(jedis.hvals(value));
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:value(" + value + ");", e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
    	
    	return result;
    }

    /**
     * 读取所有属性值并转换为 Map 对象
     *
     * @return 所有属性值
     */
    public Map<String, String> getAll() 
    {

    	Map<String, String> result ;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = jedis.hgetAll(mapName);
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:getAll(" + mapName + ");", e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
    	
    	return result;
    }
    
    public long size() {

    	long result = 0 ;
    	
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			result = jedis.hlen( this.mapName ) ;
		}catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:size(" + this.mapName + ")", e);
			throw e;
		} finally {
			release(jedis, isBroken);
		}
		return result;
    }
    
    public long getTTL()
    {
    	long seconds = 0 ;
    	boolean isBroken = false;
		Jedis jedis = null;
		try
		{
			jedis = pool.getResource() ;
			seconds = jedis.ttl( mapName ) ;
		}
		catch (JedisException e) {
			isBroken = true;
			logger.warn("failed:getTTL(" + mapName + ");", e);
			throw e;
		} 
		finally 
		{
			release(jedis, isBroken);
		}
		
		return seconds ;
    }
    
    
    
    
	/**
	 * 
	 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线   
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * *************************************************************************************************************************************
	 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 * 华丽的测试分割线 
	 * 
	 **/
	
    
    public static void main(String[] args) {
    	JedisPool jedisPool = new JedisPool( new JedisPoolConfig(), "10.100.112.102", 6381, 12, "123456", 0) ;
    	RedisHashMap map = new RedisHashMap(jedisPool, "BBB", 20 ) ;
    	map.put("aaa", "cccc");
    	System.err.println(  map.getTTL() );
    	map.put("BBB", "cccc");
    	System.err.println(  map.getTTL() );
	}
}
